package com.ficus.road.maintain.service.ops;

import cn.hutool.core.lang.Pair;
import com.ficus.road.maintain.core.model.Car;
import com.ficus.road.maintain.core.model.GpsHistory;
import com.ficus.road.maintain.dao.CarDao;
import com.ficus.road.maintain.dao.GpsHistoryDao;
import com.ficus.road.maintain.dao.ops.OpsJumpPointDao;
import com.ficus.road.maintain.dto.OpsJumpPoint;
import com.ficus.road.maintain.util.LngLonUtil;
import com.ficus.road.maintain.util.MyDateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author xiawen yang
 * @date 2022/11/15 下午2:26
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OpsJumpPointService {

    private final OpsJumpPointDao opsJumpPointDao;
    private final GpsHistoryDao gpsHistoryDao;
    private final CarDao carDao;

    public void jumpPointStatistic(int timeRange, double distance) {
        String dateStr = MyDateUtil.dateFormatStr(new Date(), "yyyy-MM-dd");
        List<String> carIds = carDao.findAll().stream().map(Car::getId).collect(Collectors.toList());
        Date startOfDay = MyDateUtil.getStartOfDay(new Date());
        long timestamp = DateUtils.addDays(startOfDay, -timeRange).getTime() / 1000;
        List<OpsJumpPoint> opsJumpPoints = new ArrayList<>();
        for (String carId : carIds) {
            List<Pair<Integer, Integer>> idx = new ArrayList<>();
            Criteria criteria = Criteria.where("timestamp").gte(timestamp).and("car_id").is(carId);
            List<GpsHistory> gpsHistories = gpsHistoryDao.findByQuery(new Query(criteria));
            List<GpsHistory> sortedHistories = gpsHistories.stream().sorted(Comparator.comparing(GpsHistory::getTimestamp)).collect(Collectors.toList());
            for (int i = 1; i < sortedHistories.size(); i++) {
                GpsHistory pre = sortedHistories.get(i - 1);
                GpsHistory cur = sortedHistories.get(i);
                Double dis = LngLonUtil.calculateDistance(pre, cur);
                if (dis > distance) {
                    idx.add(Pair.of(i - 1, i));
                }
            }
            if (idx.size() < 3) {
                log.info("carId: {}, jump point size's: {}", carId, idx.size());
                continue;
            }
            // 加头尾哨兵
            idx.add(0, Pair.of(-1, 0));
            idx.add(idx.size(), Pair.of(sortedHistories.size() - 1, -1));
            List<OpsJumpPoint.JumpPoint> jumpPoints = new ArrayList<>();
            for (int i = 1; i < idx.size() - 1; i++) {
                Pair<Integer, Integer> prePair = idx.get(i - 1);
                Pair<Integer, Integer> curPair = idx.get(i);
                Pair<Integer, Integer> postPair = idx.get(i + 1);
                int preCutOff = Math.max(curPair.getKey() - 14, prePair.getValue());
                int postCutOff = Math.min(curPair.getValue() + 15, postPair.getKey());
                int startIdx = curPair.getKey() - preCutOff;
                int endIdx = curPair.getValue() - preCutOff;
                if (postCutOff - preCutOff == 1 && postCutOff + 1 < sortedHistories.size()) {
                    postCutOff += 1;
                }
                List<GpsHistory> originData = sortedHistories.subList(preCutOff, postCutOff);
                List<OpsJumpPoint.GpsInfo> data = originData.stream().map(gpsHistory ->
                        OpsJumpPoint.GpsInfo.builder()
                                .longitude(gpsHistory.getLongitude())
                                .latitude(gpsHistory.getLatitude())
                                .timestamp(gpsHistory.getTimestamp())
                                .build()).collect(Collectors.toList());
                OpsJumpPoint.JumpPoint jumpPoint = OpsJumpPoint.JumpPoint.builder().startIdx(startIdx).endIdx(endIdx).data(data).build();
                jumpPoints.add(jumpPoint);
            }
            OpsJumpPoint opsJumpPoint = OpsJumpPoint.builder().jumpPoints(jumpPoints).carId(carId).date(dateStr).build();
            opsJumpPoints.add(opsJumpPoint);
        }
        opsJumpPointDao.insert(opsJumpPoints);
    }
}
